﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace SimplexEngine
{
    public class Camera : SceneObject
    {
        public Camera(String scene_object_name) : base(scene_object_name)
        { 
        }

        public virtual Boolean CanDraw
        {
            get 
            {
                return (parent != null && parent.Creator != null);
            }
        }

        protected override void OnUpdate(GameTime game_time, Int64 frame_number)
        {
            // build matrix
            BuildMatrix();
            // update frustum
            frustum = new BoundingFrustum(view_matrix * projection_matrix);
        }

        /// <summary>
        /// camera projection type
        /// </summary>
        public enum CameraProjectionType
        {
            Orthographic,
            Perspective
        }

        /// <summary>
        /// camera projection type
        /// </summary>
        virtual public CameraProjectionType ProjectionType
        {
            get { return projection_type; }
            set { projection_type = value; }
        }
        protected CameraProjectionType projection_type = CameraProjectionType.Perspective;

        /// <summary>
        /// build view and projection matrix
        /// </summary>
        virtual protected void BuildMatrix()
        {
            // have parent?
            if (Parent != null)
                // build view matrix by world transform matrix
                view_matrix = Matrix.Invert(Parent.Transform);
            else
                // use identity matrix
                view_matrix = Matrix.Identity;

            // projection type?
            if (projection_type == CameraProjectionType.Perspective)
                // build perspective matrix
                projection_matrix = Matrix.CreatePerspectiveFieldOfView(view_field, view_port.AspectRatio, NearPlane, FarPlane);
            else if (projection_type == CameraProjectionType.Orthographic)
                // build orthographic matrix
                projection_matrix = Matrix.CreateOrthographic(view_port.Width, view_port.Height, NearPlane, FarPlane);
        }

        internal protected void ViewAreaChanged(ViewArea view_area)
        {
            view_port = view_area.ViewPort;
        }

        protected Viewport view_port;

        /// <summary>
        /// return frustum of camera
        /// </summary>
        /// <returns></returns>
        virtual public BoundingFrustum Frustum
        {
            get { return frustum; }
        }
        protected BoundingFrustum frustum;

        /// <summary>
        /// view field
        /// </summary>
        virtual public float ViewField
        {
            get { return view_field; }
            set { view_field = value; }
        }
        protected float view_field = MathHelper.ToRadians(45.0f);

        /// <summary>
        /// near plane
        /// </summary>
        virtual public float NearPlane
        {
            get { return near_plane; }
            set { near_plane = value; }
        }
        protected float near_plane = 1.0f;

        /// <summary>
        /// far plane
        /// </summary>
        virtual public float FarPlane
        {
            get { return far_plane; }
            set { far_plane = value; }
        }
        protected float far_plane = 100.0f;

        /// <summary>
        /// view matrix
        /// </summary>
        public Matrix ViewMatrix
        {
            get { return view_matrix; }
        }
        protected Matrix view_matrix;
        // projection matrix

        /// <summary>
        /// project matrix
        /// </summary>
        public Matrix ProjectionMatrix
        {
            get { return projection_matrix; }
        }
        protected Matrix projection_matrix;

        public ContainmentType Contain(BoundingBox target)
        {
            return frustum.Contains(target);
        }

        public virtual int ViewFilter { get { return view_filter; } set { view_filter = value; } }
        protected int view_filter = (int)SceneObjectFilter.ENTITY | (int)SceneObjectFilter.LIGHT;

        public ContainmentType Contain(SceneObject target)        
        {
            if (target.Parent != null)
                if ((target.ObjectFilter & view_filter) != 0)
                    return frustum.Contains(target.Parent.AABB);

            return ContainmentType.Disjoint;
        }

        public override int ObjectFilter { get { return (int)SceneObjectFilter.CAMERA; } }
    }

    public class CameraFactory : SceneObjectFactory 
    {
        public override string Type
        {
            get { return "CAMERA"; }
        }

        public override SceneObject CreateSceneObject(String name, Dictionary<String, String> parameters)
        {
            Camera camera = new Camera(name);

            if (parameters.ContainsKey("ViewFilter"))
                camera.ViewFilter = int.Parse(parameters["ViewFilter"]);

            return camera;
        }
    }
}
